'use client';

import { use, useEffect, useMemo, useState, useRef, useCallback } from 'react';
import { createPortal } from 'react-dom';
import * as signalR from '@microsoft/signalr';
import { Pause, Play, SkipForward, Bell, BellOff, Volume2, Image as ImageIcon, MoreVertical, X, AlertTriangle, Wifi, WifiOff, ChevronUp, ChevronDown } from 'lucide-react';
import { DonationAlertData, DonationRemoteState, AlertQueueItem, AlertStatusKey, RemoteCrewItem, RemoteCrewMembersResponse, RemoteCrewMemberItem } from '@/types/donation';
import { fetchApi } from '@/lib/utils/client';
import './style.scss';

type Props = {
	params: Promise<{ widgetToken: string }>;
};

const STATUS_LABEL: Record<AlertStatusKey, string> = {
	queued: '대기',
	playing: '재생 중',
	delivered: '완료',
	skipped: '건너뜀',
	ignored: '무시',
	failed: '실패'
};

type PlayingMeta = { startedAt: number; durationSec: number };

/**
 * 둥근 시계형 카운트다운 — 보라색 호가 시계방향으로 차오르며 남은 초 표시.
 * 매 250ms 다시 렌더링 (성능 negligible).
 */
function TimerRing({ meta }: { meta: PlayingMeta }) {
	const [now, setNow] = useState<number>(() => Date.now());
	useEffect(() => {
		const id = setInterval(() => setNow(Date.now()), 250);
		return () => clearInterval(id);
	}, []);

	const elapsedSec = (now - meta.startedAt) / 1000;
	const remainingSec = Math.max(0, meta.durationSec - elapsedSec);
	const progress = Math.min(1, elapsedSec / meta.durationSec);

	const r = 12;
	const c = 2 * Math.PI * r;
	const offset = c * (1 - progress);

	return (
		<span className="dpot-remote__timer-ring" aria-label={`남은 시간 ${Math.ceil(remainingSec)}초`}>
			<svg width="30" height="30" viewBox="0 0 30 30">
				<circle cx="15" cy="15" r={r} fill="none" stroke="rgba(255,255,255,0.15)" strokeWidth="2.5" />
				<circle
					cx="15" cy="15" r={r}
					fill="none"
					stroke="#7c3aed"
					strokeWidth="2.5"
					strokeLinecap="round"
					strokeDasharray={c}
					strokeDashoffset={offset}
					transform="rotate(-90 15 15)"
					style={{ transition: 'stroke-dashoffset 0.25s linear' }}
				/>
			</svg>
			<span className="dpot-remote__timer-num">{Math.ceil(remainingSec)}</span>
		</span>
	);
}

type TabKey = 'all'|'queued'|'ignored'|'failed'|'skipped'|'delivered';

const TAB_DEFS: { key: TabKey; label: string }[] = [
	{ key: 'all', label: '전체' },
	{ key: 'queued', label: '대기' },
	{ key: 'ignored', label: '무시' },
	{ key: 'failed', label: '실패' },
	{ key: 'skipped', label: '건너뜀' },
	{ key: 'delivered', label: '완료' }
];

export default function RemotePage({ params }: Props)
{
	const { widgetToken } = use(params);
	const apiBase = '/api/donation/remote';
	const hubUrl = process.env.NEXT_PUBLIC_API_URL + '/hubs/donation';

	const [connected, setConnected] = useState(false);
	const [state, setState] = useState<DonationRemoteState>({ isPaused: false, isAccepting: true, isAudioOnly: false, isVideoOnly: false });
	const [queue, setQueue] = useState<AlertQueueItem[]>([]);
	const [openMenuID, setOpenMenuID] = useState<number|null>(null);
	const [menuPos, setMenuPos] = useState<{ top: number; right: number }|null>(null);
	const [activeTab, setActiveTab] = useState<TabKey>('all');
	const [isPopup, setIsPopup] = useState(false);
	const [playingMeta, setPlayingMeta] = useState<Record<number, PlayingMeta>>({});

	// 통합 테스트 후원 form (selectedCrewID null 이면 일반, 값 있으면 크루 후원)
	const [testName, setTestName] = useState('');
	const [testAmount, setTestAmount] = useState<number>(1000);
	const [testMessage, setTestMessage] = useState('');
	const [isTestMode, setIsTestMode] = useState(true);
	const [testSubmitting, setTestSubmitting] = useState(false);
	const [crews, setCrews] = useState<RemoteCrewItem[]>([]);
	const [crewMembers, setCrewMembers] = useState<RemoteCrewMemberItem[]>([]);
	const [selectedCrewID, setSelectedCrewID] = useState<number|null>(null);
	const [selectedMemberID, setSelectedMemberID] = useState<number|null>(null);

	const connectionRef = useRef<signalR.HubConnection|null>(null);

	useEffect(() => {
		setIsPopup(typeof window !== 'undefined' && !!window.opener);
	}, []);

	// 메뉴 외부 클릭 시 닫기
	useEffect(() => {
		if (openMenuID === null) {
			return;
		}
		const handler = (e: MouseEvent) => {
			const target = e.target as HTMLElement;
			if (!target.closest('.dpot-remote__menu-dropdown') && !target.closest('.dpot-remote__menu-btn')) {
				setOpenMenuID(null);
				setMenuPos(null);
			}
		};
		document.addEventListener('click', handler);
		return () => document.removeEventListener('click', handler);
	}, [openMenuID]);

	const toggleMenu = (alertID: number, e: React.MouseEvent<HTMLButtonElement>) => {
		if (openMenuID === alertID) {
			setOpenMenuID(null);
			setMenuPos(null);
			return;
		}
		const rect = e.currentTarget.getBoundingClientRect();
		setMenuPos({
			top: rect.bottom + 4,
			right: window.innerWidth - rect.right
		});
		setOpenMenuID(alertID);
	};

	const closeMenu = () => {
		setOpenMenuID(null);
		setMenuPos(null);
	};

	const loadState = useCallback(async () => {
		try {
			const res = await fetchApi<DonationRemoteState & { queue: AlertQueueItem[] }>(`${apiBase}/state/${widgetToken}`, { silent: true });
			if (res.data) {
				setState({ isPaused: res.data.isPaused, isAccepting: res.data.isAccepting, isAudioOnly: res.data.isAudioOnly, isVideoOnly: res.data.isVideoOnly });
				setQueue(res.data.queue || []);
			}
		} catch {}
	}, [apiBase, widgetToken]);

	const loadCrews = useCallback(async () => {
		try {
			const res = await fetchApi<{ crews: RemoteCrewItem[] }>(`${apiBase}/crews/${widgetToken}`, { silent: true });
			if (res.data) {
				setCrews(res.data.crews);
			}
		} catch {}
	}, [apiBase, widgetToken]);

	const loadCrewMembers = useCallback(async (crewID: number) => {
		try {
			const res = await fetchApi<RemoteCrewMembersResponse>(`${apiBase}/crews/${widgetToken}/${crewID}/members`, { silent: true });
			if (res.data) {
				setCrewMembers(res.data.members);
				setSelectedMemberID(null);
			}
		} catch {}
	}, [apiBase, widgetToken]);

	const connectHub = useCallback(() => {
		const conn = new signalR.HubConnectionBuilder().withUrl(hubUrl).withAutomaticReconnect().build();

		conn.on('ReceiveAlert', (data: DonationAlertData) => {
			setQueue(prev => {
				const existing = prev.find(q => q.alertID === data.alertID);
				if (existing) {
					return prev.map(q => q.alertID === data.alertID ? { ...q, status: 'queued' as AlertStatusKey } : q);
				}
				const item: AlertQueueItem = {
					alertID: data.alertID,
					donationID: data.donationID,
					status: 'queued',
					sponsorMemberID: data.sponsorMemberID,
					sendName: data.sendName,
					amount: data.amount,
					message: data.message,
					isTest: data.isTest,
					createdAt: data.createdAt
				};
				return [item, ...prev];
			});
		});

		conn.on('ReceiveState', (s: DonationRemoteState) => setState(s));
		conn.on('ReceiveSkip', () => {
			setQueue(prev => prev.map(q => q.status === 'playing' ? { ...q, status: 'skipped' } : q));
		});

		conn.on('ReceiveQueueReorder', (data: { swap?: number[] }) => {
			if (!data?.swap || data.swap.length !== 2) {
				return;
			}
			const [a, b] = data.swap;
			setQueue(prev => {
				const idxA = prev.findIndex(q => q.alertID === a);
				const idxB = prev.findIndex(q => q.alertID === b);
				if (idxA < 0 || idxB < 0) {
					return prev;
				}
				const next = [...prev];
				[next[idxA], next[idxB]] = [next[idxB], next[idxA]];
				return next;
			});
		});

		conn.on('ReceiveAlertStatusUpdate', (update: { alertID: number; status: AlertStatusKey; displayDurationSec?: number|null; startedAt?: string|null }) => {
			setQueue(prev => prev.map(q => q.alertID === update.alertID ? { ...q, status: update.status } : q));

			// playing 진입 시 타이머 ring 시작용 메타 저장
			if (update.status === 'playing' && update.displayDurationSec && update.displayDurationSec > 0) {
				const started = update.startedAt ? new Date(update.startedAt).getTime() : Date.now();
				setPlayingMeta(prev => ({ ...prev, [update.alertID]: { startedAt: started, durationSec: update.displayDurationSec! } }));
			} else if (update.status !== 'playing') {
				// playing 종료 → 메타 제거 (memory leak 방지)
				setPlayingMeta(prev => {
					if (!(update.alertID in prev)) {
						return prev;
					}
					const next = { ...prev };
					delete next[update.alertID];
					return next;
				});
			}
		});

		conn.start().then(() => {
			conn.invoke('JoinChannel', widgetToken);
			setConnected(true);
		}).catch(() => {});

		conn.onclose(() => setConnected(false));
		conn.onreconnected(() => { conn.invoke('JoinChannel', widgetToken); setConnected(true); });
		connectionRef.current = conn;
	}, [hubUrl, widgetToken]);

	useEffect(() => {
		loadState();
		loadCrews();
		connectHub();
		return () => { connectionRef.current?.stop(); };
	}, [loadState, loadCrews, connectHub]);

	useEffect(() => {
		if (selectedCrewID) {
			loadCrewMembers(selectedCrewID);
		} else {
			setCrewMembers([]);
			setSelectedMemberID(null);
		}
	}, [selectedCrewID, loadCrewMembers]);

	const togglePause = async () => {
		const next = { ...state, isPaused: !state.isPaused };
		setState(next);
		await fetchApi(`${apiBase}/state`, { method: 'POST', body: { ...next, widgetToken }, silent: true });
	};

	const toggleAccepting = async () => {
		const next = { ...state, isAccepting: !state.isAccepting };
		setState(next);
		await fetchApi(`${apiBase}/state`, { method: 'POST', body: { ...next, widgetToken }, silent: true });
	};

	const toggleAudioOnly = async () => {
		const next = { ...state, isAudioOnly: !state.isAudioOnly, isVideoOnly: false };
		setState(next);
		await fetchApi(`${apiBase}/state`, { method: 'POST', body: { ...next, widgetToken }, silent: true });
	};

	const toggleVideoOnly = async () => {
		const next = { ...state, isVideoOnly: !state.isVideoOnly, isAudioOnly: false };
		setState(next);
		await fetchApi(`${apiBase}/state`, { method: 'POST', body: { ...next, widgetToken }, silent: true });
	};

	const skipCurrent = async () => {
		await fetchApi(`${apiBase}/skip/${widgetToken}`, { method: 'POST', silent: true });
	};

	const ignoreAlert = async (alertID: number) => {
		await fetchApi(`${apiBase}/ignore/${alertID}`, { method: 'POST', silent: true });
		setQueue(prev => prev.map(q => q.alertID === alertID ? { ...q, status: 'ignored' } : q));
		closeMenu();
	};

	// 일시정지 중일 때만, queued 상태 인접 알림과 swap
	const swapAlert = async (alertID: number, direction: 'up'|'down') => {
		const queuedItems = filtered.filter(q => q.status === 'queued');
		const idx = queuedItems.findIndex(q => q.alertID === alertID);
		if (idx < 0) {
			return;
		}
		const targetIdx = direction === 'up' ? idx - 1 : idx + 1;
		if (targetIdx < 0 || targetIdx >= queuedItems.length) {
			return;
		}
		const partner = queuedItems[targetIdx];
		await fetchApi(`${apiBase}/reorder`, {
			method: 'POST',
			body: { widgetToken, alertIDA: alertID, alertIDB: partner.alertID },
			silent: true
		});
		// 백엔드가 broadcast 한 ReceiveQueueReorder 가 도착하면 화면 갱신됨
	};

	const resendAlert = async (alertID: number) => {
		await fetchApi(`${apiBase}/resend/${alertID}`, { method: 'POST', silent: true });
		setQueue(prev => prev.map(q => q.alertID === alertID ? { ...q, status: 'queued' } : q));
		closeMenu();
	};

	const playNow = async (alertID: number) => {
		closeMenu();
		// status 변경은 SignalR ReceiveAlertStatusUpdate에 맡김 (위젯이 재생 시작 → 'playing' broadcast)
		// 직접 setQueue 하면 race condition으로 'playing'을 덮어씀
		await fetchApi(`${apiBase}/play-now/${alertID}`, { method: 'POST', silent: true });
	};

	const markDelivered = async (alertID: number) => {
		closeMenu();
		// Playing 상태에서 stuck된 알림 강제 완료 처리 (위젯 종료/네트워크 단절 등)
		// status 변경은 SignalR broadcast로 동기화
		await fetchApi(`${apiBase}/mark-delivered/${alertID}`, { method: 'POST', silent: true });
	};

	const submitTestDonate = async () => {
		if (!testName.trim()) {
			alert('별명을 입력하세요.');
			return;
		}
		if (testAmount < 1000) {
			alert('최소 후원 금액은 1,000원입니다.');
			return;
		}
		if (selectedCrewID && (selectedMemberID === null || selectedMemberID <= 0)) {
			alert('크루원을 선택하세요.');
			return;
		}
		if (!isTestMode) {
			const isCrew = !!selectedCrewID;
			const msg = isCrew
				? '영구 후원으로 기록됩니다. 활성 크루 세션이 있으면 리더보드에 합산됩니다. 계속하시겠습니까?'
				: '영구 후원으로 기록됩니다. 본인 채널 통계에 반영됩니다. 계속하시겠습니까?';
			const ok = window.confirm(msg);
			if (!ok) {
				return;
			}
		}
		setTestSubmitting(true);
		try {
			await fetchApi(`${apiBase}/test-donate`, {
				method: 'POST',
				body: {
					widgetToken,
					sendName: testName.trim(),
					amount: testAmount,
					message: testMessage.trim() || null,
					crewMemberID: selectedCrewID ? selectedMemberID : null,
					isTest: isTestMode
				},
				silent: false
			});
			setTestMessage('');
		} catch (e) {
			console.error('test-donate failed:', e);
		} finally {
			setTestSubmitting(false);
		}
	};

	const counts = useMemo(() => ({
		all: queue.length,
		queued: queue.filter(q => q.status === 'queued' || q.status === 'playing').length,
		ignored: queue.filter(q => q.status === 'ignored').length,
		failed: queue.filter(q => q.status === 'failed').length,
		skipped: queue.filter(q => q.status === 'skipped').length,
		delivered: queue.filter(q => q.status === 'delivered').length
	}), [queue]);

	const filtered = useMemo(() => {
		if (activeTab === 'all') {
			return queue;
		}
		if (activeTab === 'queued') {
			return queue.filter(q => q.status === 'queued' || q.status === 'playing');
		}
		return queue.filter(q => q.status === activeTab);
	}, [activeTab, queue]);

	const formatTime = (dateStr: string) => {
		const d = new Date(dateStr);
		return `${d.getHours().toString().padStart(2, '0')}:${d.getMinutes().toString().padStart(2, '0')}`;
	};

	return (
		<div className="dpot-remote">
			{/* 상단 상태 배너 */}
			<div className={`dpot-remote__banner${state.isPaused ? ' dpot-remote__banner--paused' : ' dpot-remote__banner--playing'}`} role="status">
				{state.isPaused ? (
					<><Pause size={14} aria-hidden="true" /><span>현재 알림 일시정지 중입니다</span></>
				) : (
					<><Play size={14} aria-hidden="true" /><span>현재 알림 재생 중입니다</span></>
				)}
			</div>

			{/* 헤더 */}
			<header className="dpot-remote__header">
				<div className="dpot-remote__brand">
					<span className="dpot-remote__brand-logo">DPOT</span>
					<span className="dpot-remote__brand-title">리모콘</span>
				</div>
				<div className="dpot-remote__header-actions">
					<span className={`dpot-remote__connection${connected ? ' dpot-remote__connection--on' : ' dpot-remote__connection--off'}`}>
						{connected ? <Wifi size={12} aria-hidden="true" /> : <WifiOff size={12} aria-hidden="true" />}
						<span>{connected ? '연결됨' : '연결 끊김'}</span>
					</span>
					{isPopup && (
						<button type="button" className="dpot-remote__close-btn" onClick={() => window.close()} aria-label="닫기">
							<X size={16} aria-hidden="true" />
						</button>
					)}
				</div>
			</header>

			{/* 운영 토글 (음성만/영상만/후원수신) */}
			<div className="dpot-remote__sub-controls">
				<button type="button" className={`dpot-remote__sub-btn${state.isAccepting ? '' : ' dpot-remote__sub-btn--off'}`} onClick={toggleAccepting}>
					{state.isAccepting ? <Bell size={14} aria-hidden="true" /> : <BellOff size={14} aria-hidden="true" />}
					<span>{state.isAccepting ? '후원 받는 중' : '후원 안 받음'}</span>
				</button>
				<button type="button" className={`dpot-remote__sub-btn${state.isAudioOnly ? ' dpot-remote__sub-btn--active' : ''}`} onClick={toggleAudioOnly}>
					<Volume2 size={14} aria-hidden="true" />
					<span>음성만</span>
				</button>
				<button type="button" className={`dpot-remote__sub-btn${state.isVideoOnly ? ' dpot-remote__sub-btn--active' : ''}`} onClick={toggleVideoOnly}>
					<ImageIcon size={14} aria-hidden="true" />
					<span>영상만</span>
				</button>
			</div>

			{/* 통합 테스트 후원 form (크루 미선택=일반, 선택=크루) */}
			<fieldset className="dpot-remote__test-form">
				<legend className="dpot-remote__test-form-legend">테스트 후원</legend>
				<div className="dpot-remote__test-row">
					<select
						className="dpot-remote__select"
						value={selectedCrewID ?? ''}
						onChange={e => setSelectedCrewID(e.target.value ? parseInt(e.target.value, 10) : null)}
					>
						<option value="">크루 없음 (일반 후원)</option>
						{crews.map(c => (
							<option key={c.id} value={c.id}>크루 · {c.name} ({c.memberCount}명)</option>
						))}
					</select>
					<select
						className="dpot-remote__select"
						value={selectedMemberID ?? ''}
						onChange={e => setSelectedMemberID(e.target.value ? parseInt(e.target.value, 10) : null)}
						disabled={!selectedCrewID || crewMembers.length === 0}
					>
						<option value="">{selectedCrewID ? '크루원 선택' : '— 크루원 —'}</option>
						{crewMembers.map(m => (
							<option key={m.id} value={m.id}>{m.nickname}{m.role ? ` (${m.role})` : ''}</option>
						))}
					</select>
				</div>
				<div className="dpot-remote__test-row">
					<input
						type="text"
						className="dpot-remote__input"
						placeholder="별명"
						value={testName}
						onChange={e => setTestName(e.target.value)}
						maxLength={30}
					/>
					<input
						type="number"
						className="dpot-remote__input dpot-remote__input--amount"
						placeholder="금액"
						value={testAmount > 0 ? testAmount : ''}
						onChange={e => setTestAmount(parseInt(e.target.value || '0', 10))}
						min={1000}
						step={1000}
					/>
				</div>
				<textarea
					className="dpot-remote__textarea"
					placeholder="메시지 (선택)"
					value={testMessage}
					onChange={e => setTestMessage(e.target.value)}
					rows={2}
					maxLength={200}
				/>
				<div className="dpot-remote__test-form-footer">
					<label className="dpot-remote__checkbox">
						<input
							type="checkbox"
							checked={isTestMode}
							onChange={e => setIsTestMode(e.target.checked)}
						/>
						<span>테스트 모드 (위젯에만 표시, DB 통계 미반영)</span>
					</label>
					<button
						type="button"
						className="dpot-remote__test-submit"
						onClick={submitTestDonate}
						disabled={testSubmitting}
					>
						{testSubmitting ? '전송 중...' : '후원하기'}
					</button>
				</div>
				{!isTestMode && (
					<p className="dpot-remote__warn" role="alert">
						<AlertTriangle size={14} aria-hidden="true" />
						<span>
							{selectedCrewID
								? '영구 후원으로 기록됩니다. 활성 크루 세션이 있을 경우 리더보드에 합산됩니다.'
								: '영구 후원으로 기록됩니다. 통계에 반영됩니다.'}
						</span>
					</p>
				)}
			</fieldset>

			{/* 6개 탭 */}
			<div className="dpot-remote__tabs" role="tablist">
				{TAB_DEFS.map(t => (
					<button
						key={t.key}
						type="button"
						role="tab"
						aria-selected={activeTab === t.key}
						className={`dpot-remote__tab${activeTab === t.key ? ' dpot-remote__tab--active' : ''}`}
						onClick={() => setActiveTab(t.key)}
					>
						<span className="dpot-remote__tab-label">{t.label}</span>
						<span className="dpot-remote__tab-count">{counts[t.key]}</span>
					</button>
				))}
			</div>

			{/* 후원 알림 listing */}
			<div className="dpot-remote__list-wrap">
				<div className="dpot-remote__list-header" role="row">
					<span className="dpot-remote__col dpot-remote__col--reorder">순서</span>
					<span className="dpot-remote__col dpot-remote__col--name">별명</span>
					<span className="dpot-remote__col dpot-remote__col--amount">금액</span>
					<span className="dpot-remote__col dpot-remote__col--msg">메시지</span>
					<span className="dpot-remote__col dpot-remote__col--time">시간</span>
					<span className="dpot-remote__col dpot-remote__col--status">상태</span>
					<span className="dpot-remote__col dpot-remote__col--menu">기능</span>
				</div>
				<div className="dpot-remote__list">
					{filtered.length === 0 && <div className="dpot-remote__empty">후원 알림이 없습니다</div>}
					{filtered.map(item => {
						// queued 항목 중 reorder 가능한 위치 판정
						const queuedItems = filtered.filter(q => q.status === 'queued');
						const qIdx = queuedItems.findIndex(q => q.alertID === item.alertID);
						const canMoveUp = item.status === 'queued' && qIdx > 0;
						const canMoveDown = item.status === 'queued' && qIdx >= 0 && qIdx < queuedItems.length - 1;
						return (
						<div key={item.alertID} className={`dpot-remote__row dpot-remote__row--${item.status}`} role="row">
							<span className="dpot-remote__col dpot-remote__col--reorder">
								{item.status === 'queued' ? (
									<>
										<button
											type="button"
											className="dpot-remote__reorder-btn"
											onClick={() => swapAlert(item.alertID, 'up')}
											disabled={!canMoveUp}
											aria-label="위로"
											title="위로"
										>
											<ChevronUp size={14} aria-hidden="true" />
										</button>
										<button
											type="button"
											className="dpot-remote__reorder-btn"
											onClick={() => swapAlert(item.alertID, 'down')}
											disabled={!canMoveDown}
											aria-label="아래로"
											title="아래로"
										>
											<ChevronDown size={14} aria-hidden="true" />
										</button>
									</>
								) : null}
							</span>
							<span className="dpot-remote__col dpot-remote__col--name" title={item.sendName}>
								{item.isTest && <span className="dpot-remote__test-badge">TEST</span>}
								{item.sendName}
							</span>
							<span className="dpot-remote__col dpot-remote__col--amount">{item.amount.toLocaleString()}원</span>
							<span className="dpot-remote__col dpot-remote__col--msg" title={item.message ?? ''}>{item.message ?? '—'}</span>
							<span className="dpot-remote__col dpot-remote__col--time">{formatTime(item.createdAt)}</span>
							<span className={`dpot-remote__col dpot-remote__col--status dpot-remote__status-badge dpot-remote__status-badge--${item.status}`}>
								{item.status === 'playing' && playingMeta[item.alertID]
									? <TimerRing meta={playingMeta[item.alertID]} />
									: (STATUS_LABEL[item.status] ?? item.status)}
							</span>
							<span className="dpot-remote__col dpot-remote__col--menu">
								<button
									type="button"
									className="dpot-remote__menu-btn"
									onClick={(e) => toggleMenu(item.alertID, e)}
									aria-label="메뉴 열기"
									aria-expanded={openMenuID === item.alertID}
								>
									<MoreVertical size={16} aria-hidden="true" />
								</button>
							</span>
						</div>
						);
					})}
				</div>
			</div>

			{/* 하단 메인 컨트롤 */}
			<div className="dpot-remote__main-controls">
				<button
					type="button"
					className={`dpot-remote__main-btn dpot-remote__main-btn--pause${state.isPaused ? ' dpot-remote__main-btn--active' : ''}`}
					onClick={() => { if (!state.isPaused) togglePause(); }}
					disabled={state.isPaused}
				>
					<Pause size={18} aria-hidden="true" />
					<span>일시정지</span>
				</button>
				<button
					type="button"
					className="dpot-remote__main-btn dpot-remote__main-btn--skip"
					onClick={skipCurrent}
				>
					<SkipForward size={18} aria-hidden="true" />
					<span>건너뛰기</span>
				</button>
				<button
					type="button"
					className={`dpot-remote__main-btn dpot-remote__main-btn--play${!state.isPaused ? ' dpot-remote__main-btn--active' : ''}`}
					onClick={() => { if (state.isPaused) togglePause(); }}
					disabled={!state.isPaused}
				>
					<Play size={18} aria-hidden="true" />
					<span>재생</span>
				</button>
			</div>

			{/* 메뉴 dropdown — Portal로 body에 렌더해서 list overflow 클립 회피 */}
			{openMenuID !== null && menuPos && typeof document !== 'undefined' && (() => {
				const activeItem = queue.find(q => q.alertID === openMenuID);
				if (!activeItem) {
					return null;
				}
				return createPortal(
					<div
						className="dpot-remote__menu-dropdown"
						role="menu"
						style={{ position: 'fixed', top: menuPos.top, right: menuPos.right }}
					>
						{activeItem.status === 'queued' && (
							<>
								<button type="button" className="dpot-remote__menu-item" onClick={() => playNow(activeItem.alertID)}>즉시 재생</button>
								<button type="button" className="dpot-remote__menu-item dpot-remote__menu-item--danger" onClick={() => ignoreAlert(activeItem.alertID)}>무시</button>
							</>
						)}
						{activeItem.status === 'playing' && (
							<button type="button" className="dpot-remote__menu-item" onClick={() => markDelivered(activeItem.alertID)}>완료 처리</button>
						)}
						{(activeItem.status === 'failed' || activeItem.status === 'skipped' || activeItem.status === 'ignored' || activeItem.status === 'delivered') && (
							<button type="button" className="dpot-remote__menu-item" onClick={() => resendAlert(activeItem.alertID)}>재전송</button>
						)}
						{activeItem.status === 'skipped' && (
							<button type="button" className="dpot-remote__menu-item dpot-remote__menu-item--danger" onClick={() => ignoreAlert(activeItem.alertID)}>무시</button>
						)}
					</div>,
					document.body
				);
			})()}
		</div>
	);
}
